package main

import (
	"bufio"
	"fmt"
	"math"
	"os"
	"runtime"
	"strconv"
	"strings"
)

// （这是一种综合练习，使用到第 4、9、11 章和本章的内容。）写一个可交互的控制台程序，要求用户输入二位平面极坐标上的点（半径和角度（度））。
// 计算对应的笛卡尔坐标系的点的 x 和 y 并输出。使用极坐标和笛卡尔坐标的结构体。

// 使用通道和协程：

// channel1 用来接收极坐标
// channel2 用来接收笛卡尔坐标

// 转换过程需要在协程中进行，从 channel1 中读取然后发送到 channel2。
// 实际上做这种计算不提倡使用协程和通道，但是如果运算量很大很耗时，这种方案设计就非常合适了。

type polar struct {
	radius, θ float64
}

type cartesian struct {
	x, y float64
}

const result = "极坐标: 半径=%0.2f 角度=%0.2f 度 -- 笛卡尔坐标: x=%.02f, y=%0.2f\n"

var tips = "输入半径和角度值, 例如: 12.5 90, " + "或%s退出"

func init() {
	if runtime.GOOS == "windows" {
		tips = fmt.Sprintf(tips, "Ctrl+Z, Enter")
	} else {
		tips = fmt.Sprintf(tips, "Ctrl+D")
	}
}

func main() {
	input := make(chan polar)
	defer close(input)
	output := trans(input)
	defer close(output)
	interact(input, output)
}

func trans(pol chan polar) chan cartesian {
	res := make(chan cartesian)
	go func() {
		for {
			p := <-pol
			θ := p.θ * math.Pi / 180
			x := p.radius * math.Cos(θ)
			y := p.radius * math.Sin(θ)
			res <- cartesian{x, y}
		}

	}()
	return res
}

func interact(in chan polar, out chan cartesian) {
	// 创建读取输入的句柄
	reader := bufio.NewReader(os.Stdin)
	// 打印提示语
	fmt.Println(tips)
	for {
		fmt.Print("半径和角度: ")
		// 从句柄中读取
		line, err := reader.ReadString('\n')
		if err != nil {
			fmt.Fprintf(os.Stderr, "错误的输入: %s", err)
			break
		}
		// 去除最后一位\n
		line = line[:len(line)-1]
		if fields := strings.Fields(line); len(fields) == 2 {
			floats, err := parseFloat(fields)
			if err != nil {
				fmt.Fprintln(os.Stderr, "不合法的数字")
				continue
			}
			// 通道输入
			in <- polar{floats[0], floats[1]}
			// 通道输出
			coord := <-out
			// 打印输出
			fmt.Fprintf(os.Stdout, result, floats[0], floats[1], coord.x, coord.y)
		} else {
			fmt.Fprintf(os.Stderr, "输入错误, 请输入两个数字, 以空格隔开")
			continue
		}

	}
}

func parseFloat(numbers []string) ([]float64, error) {
	var floats []float64
	for _, str := range numbers {
		if x, err := strconv.ParseFloat(str, 64); err != nil {
			return nil, err
		} else {
			floats = append(floats, x)
		}
	}
	return floats, nil
}
